<template>
  <div class="slam-visualization-3d">
    <!-- 控制面板 -->
    <div class="control-panel">
      <div class="control-header">
        <h3>SLAM 3D 可视化</h3>
        <div class="status-indicator" :class="slamStatus">
          <span class="status-dot"></span>
          {{ statusText }}
        </div>
      </div>
      
      <div class="control-buttons">
        <div class="status-info">
          <i class="fa-solid fa-info-circle mr-1"></i>
          <span>3D可视化状态跟随主控制面板</span>
        </div>
        <button @click="testTrajectory" class="test-btn">
          <i class="fa-solid fa-play mr-1"></i>
          测试轨迹
        </button>
        <button @click="reset3DVisualization" class="test-btn" style="background: #f44336;">
          <i class="fa-solid fa-refresh mr-1"></i>
          重置3D
        </button>
      </div>
      
      <!-- 数据统计 -->
      <div class="data-stats">
        <div class="stat-item">
          <label>关键帧:</label>
          <span class="stat-value">{{ slamData.keyframes }}</span>
        </div>
        <div class="stat-item">
          <label>地图点:</label>
          <span class="stat-value">{{ slamData.mapPoints }}</span>
        </div>
        <div class="stat-item">
          <label>轨迹长度:</label>
          <span class="stat-value">{{ slamData.trajectoryLength.toFixed(2) }}m</span>
        </div>
        <div class="stat-item">
          <label>进度:</label>
          <span class="stat-value">{{ slamData.progress.toFixed(1) }}%</span>
        </div>
      </div>
    </div>
    
    <!-- 3D可视化容器 -->
    <div class="visualization-container">
      <div ref="threeContainer" class="three-container"></div>
      
      <!-- 视图控制 -->
      <div class="view-controls">
        <button @click="resetCamera" class="view-btn" title="重置视角">
          <i class="icon-home"></i>
        </button>
        <button @click="toggleWireframe" class="view-btn" title="切换线框模式">
          <i class="icon-grid"></i>
        </button>
        <button @click="togglePoints" class="view-btn" title="切换地图点显示">
          <i class="icon-dots"></i>
        </button>
        <button @click="toggleTrajectory" class="view-btn" title="切换轨迹显示">
          <i class="icon-path"></i>
        </button>
      </div>
      
      <!-- 坐标轴 -->
      <div class="coordinate-axes">
        <div class="axis x-axis">X</div>
        <div class="axis y-axis">Y</div>
        <div class="axis z-axis">Z</div>
      </div>
    </div>
    
    <!-- 信息面板 -->
    <div class="info-panel">
      <div class="info-section">
        <h4>相机信息</h4>
        <div class="info-item">
          <label>位置:</label>
          <span>{{ cameraPosition.x.toFixed(2) }}, {{ cameraPosition.y.toFixed(2) }}, {{ cameraPosition.z.toFixed(2) }}</span>
        </div>
        <div class="info-item">
          <label>朝向:</label>
          <span>{{ cameraRotation.x.toFixed(2) }}, {{ cameraRotation.y.toFixed(2) }}, {{ cameraRotation.z.toFixed(2) }}</span>
        </div>
      </div>
      
      <div class="info-section">
        <h4>渲染信息</h4>
        <div class="info-item">
          <label>FPS:</label>
          <span>{{ fps }}</span>
        </div>
        <div class="info-item">
          <label>顶点数:</label>
          <span>{{ vertexCount }}</span>
        </div>
        <div class="info-item">
          <label>面数:</label>
          <span>{{ faceCount }}</span>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted, reactive, computed } from 'vue'
import * as THREE from 'three'

// 响应式数据
const threeContainer = ref(null)
const isRunning = ref(false)
const slamStatus = ref('stopped')
const fps = ref(0)
const vertexCount = ref(0)
const faceCount = ref(0)

// SLAM数据
const slamData = reactive({
  keyframes: 0,
  mapPoints: 0,
  trajectoryLength: 0.0,
  progress: 0.0
})

// 相机信息
const cameraPosition = reactive({ x: 0, y: 0, z: 0 })
const cameraRotation = reactive({ x: 0, y: 0, z: 0 })

// 3D场景相关
let scene, camera, renderer, controls
let trajectoryLine, mapPoints, keyframePoints
let animationId
let lastTime = 0
let frameCount = 0

// 显示选项
const showWireframe = ref(false)
const showPoints = ref(true)
const showTrajectory = ref(true)

// 计算属性
const statusText = computed(() => {
  switch (slamStatus.value) {
    case 'running': return '运行中'
    case 'stopped': return '已停止'
    case 'error': return '错误'
    default: return '未知'
  }
})

// 初始化3D场景
const initThreeJS = async () => {
  if (!threeContainer.value) return
  
  // 创建场景
  scene = new THREE.Scene()
  scene.background = new THREE.Color(0x1a1a1a)
  
  // 创建相机
  camera = new THREE.PerspectiveCamera(
    75, 
    threeContainer.value.clientWidth / threeContainer.value.clientHeight, 
    0.1, 
    1000
  )
  camera.position.set(10, 10, 10)
  camera.lookAt(0, 0, 0)
  
  // 创建渲染器
  renderer = new THREE.WebGLRenderer({ antialias: true })
  renderer.setSize(threeContainer.value.clientWidth, threeContainer.value.clientHeight)
  renderer.shadowMap.enabled = true
  renderer.shadowMap.type = THREE.PCFSoftShadowMap
  threeContainer.value.appendChild(renderer.domElement)
  
  // 添加轨道控制器
  const { OrbitControls } = await import('three-stdlib')
  controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true
  controls.dampingFactor = 0.05
  
  // 添加光照
  const ambientLight = new THREE.AmbientLight(0x404040, 0.6)
  scene.add(ambientLight)
  
  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8)
  directionalLight.position.set(10, 10, 5)
  directionalLight.castShadow = true
  scene.add(directionalLight)
  
  // 创建坐标轴
  const axesHelper = new THREE.AxesHelper(5)
  scene.add(axesHelper)
  
  // 初始化SLAM对象
  initSlamObjects()
  
  // 开始渲染循环
  animate()
}

// 初始化SLAM相关对象
const initSlamObjects = () => {
  // 移除轨迹线，只使用绿色框显示轨迹
  // trajectoryLine = null
  
  // 地图点云
  const mapPointsGeometry = new THREE.BufferGeometry()
  const mapPointsMaterial = new THREE.PointsMaterial({ 
    color: 0xff0000, 
    size: 0.1,
    sizeAttenuation: true
  })
  mapPoints = new THREE.Points(mapPointsGeometry, mapPointsMaterial)
  scene.add(mapPoints)
  
  // 关键帧点
  const keyframeGeometry = new THREE.BufferGeometry()
  const keyframeMaterial = new THREE.PointsMaterial({ 
    color: 0x0000ff, 
    size: 0.3,
    sizeAttenuation: true
  })
  keyframePoints = new THREE.Points(keyframeGeometry, keyframeMaterial)
  scene.add(keyframePoints)
}

// 动画循环
const animate = () => {
  animationId = requestAnimationFrame(animate)
  
  const currentTime = performance.now()
  const deltaTime = currentTime - lastTime
  lastTime = currentTime
  
  // 更新控制器
  if (controls) {
    controls.update()
  }
  
  // 更新相机信息
  updateCameraInfo()
  
  // 更新FPS
  frameCount++
  if (frameCount % 60 === 0) {
    fps.value = Math.round(1000 / deltaTime)
  }
  
  // 渲染场景
  if (renderer && scene && camera) {
    renderer.render(scene, camera)
  }
}

// 更新相机信息
const updateCameraInfo = () => {
  if (camera) {
    cameraPosition.x = camera.position.x
    cameraPosition.y = camera.position.y
    cameraPosition.z = camera.position.z
    
    cameraRotation.x = camera.rotation.x
    cameraRotation.y = camera.rotation.y
    cameraRotation.z = camera.rotation.z
  }
}

// 更新SLAM数据
const updateSlamData = (newData) => {
  console.log('🎯 3D可视化接收数据:', newData)
  
  // 更新数据
  slamData.keyframes = newData.keyframes || 0
  slamData.mapPoints = newData.mapPoints || 0
  slamData.trajectoryLength = newData.trajectoryLength || 0
  slamData.progress = newData.progress || 0
  
  // 更新轨迹点数据
  slamData.trajectoryPoints = newData.trajectoryPoints || []
  slamData.mapPoints3D = newData.mapPoints3D || []
  slamData.cameraPoses = newData.cameraPoses || []
  
  console.log('📊 3D可视化数据更新:', {
    keyframes: slamData.keyframes,
    mapPoints: slamData.mapPoints,
    trajectoryLength: slamData.trajectoryLength,
    progress: slamData.progress,
    trajectoryPoints: slamData.trajectoryPoints.length,
    mapPoints3D: slamData.mapPoints3D.length,
    cameraPoses: slamData.cameraPoses.length
  })
  
  // 更新3D可视化
  updateVisualization()
}

// 更新3D可视化
const updateVisualization = () => {
  if (!scene) {
    console.warn('⚠️ 3D场景未初始化，跳过可视化更新')
    return
  }
  
  console.log('🔄 开始更新3D可视化...')
  
  // 强制更新轨迹线
  if (trajectoryLine) {
    updateTrajectoryLine()
    console.log('✅ 轨迹线已更新')
  }
  
  // 更新地图点
  if (showPoints.value && mapPoints) {
    updateMapPoints()
    console.log('✅ 地图点已更新')
  }
  
  // 更新关键帧点
  if (keyframePoints) {
    updateKeyframePoints()
    console.log('✅ 关键帧点已更新')
  }
  
  // 更新渲染统计
  updateRenderStats()
  
  console.log('🎯 3D可视化更新完成')
}

// 更新轨迹线
const updateTrajectoryLine = () => {
  if (!trajectoryLine) return
  
  // 使用真实的轨迹数据
  const points = []
  
  console.log(`📈 更新轨迹线: 关键帧=${slamData.keyframes}, 轨迹长度=${slamData.trajectoryLength}`)
  
  // 如果有真实的轨迹数据，使用真实数据
  if (slamData.trajectoryPoints && slamData.trajectoryPoints.length > 0) {
    console.log(`🎯 使用真实轨迹数据: ${slamData.trajectoryPoints.length}个点`)
    console.log(`🔍 第一个轨迹点格式:`, slamData.trajectoryPoints[0])
    console.log(`🔍 轨迹数据详情:`, slamData.trajectoryPoints.slice(0, 3))
    slamData.trajectoryPoints.forEach((point, index) => {
      // 处理不同的数据格式
      let x, y, z
      if (Array.isArray(point)) {
        // 数组格式 [x, y, z]
        x = point[0] || 0
        y = point[1] || 0
        z = point[2] || 0
      } else if (point && typeof point === 'object') {
        // 对象格式 {x, y, z} 或 {frame_id, x, y, z}
        x = point.x || 0
        y = point.y || 0
        z = point.z || 0
      } else {
        return // 跳过无效数据
      }
      
      if (typeof x === 'number' && typeof y === 'number' && typeof z === 'number') {
        points.push(new THREE.Vector3(x, y, z))
        if (index < 5) { // 只打印前5个点的详细信息
          console.log(`📍 轨迹点${index}: (${x.toFixed(2)}, ${y.toFixed(2)}, ${z.toFixed(2)})`)
        }
      }
    })
  } else {
    // 如果没有真实数据，生成简单的轨迹
    const numPoints = Math.max(slamData.keyframes * 2, 5)
    console.log(`🎯 生成模拟轨迹数据: ${numPoints}个点`)
    
    for (let i = 0; i < numPoints; i++) {
      const t = i / Math.max(numPoints - 1, 1)
      const x = Math.sin(t * Math.PI) * 2
      const y = Math.cos(t * Math.PI) * 1.5
      const z = t * 3
      points.push(new THREE.Vector3(x, y, z))
    }
  }
  
  if (points.length > 0) {
    // 释放旧几何体，创建新的几何体
    if (trajectoryLine.geometry) {
      trajectoryLine.geometry.dispose()
    }
    trajectoryLine.geometry = new THREE.BufferGeometry()
    trajectoryLine.geometry.setFromPoints(points)
    trajectoryLine.visible = true // 强制显示
    
    // 强制更新材质
    trajectoryLine.material.needsUpdate = true
    trajectoryLine.geometry.attributes.position.needsUpdate = true
    
    console.log(`✅ 轨迹线更新完成: ${points.length}个点`)
    console.log(`📍 轨迹线范围: x[${Math.min(...points.map(p => p.x)).toFixed(2)}, ${Math.max(...points.map(p => p.x)).toFixed(2)}], y[${Math.min(...points.map(p => p.y)).toFixed(2)}, ${Math.max(...points.map(p => p.y)).toFixed(2)}], z[${Math.min(...points.map(p => p.z)).toFixed(2)}, ${Math.max(...points.map(p => p.z)).toFixed(2)}]`)
  } else {
    console.warn('⚠️ 没有轨迹点数据')
    // 即使没有数据也要显示空的轨迹线
    trajectoryLine.visible = false
  }
}

// 更新地图点
const updateMapPoints = () => {
  if (!mapPoints) return
  
  const points = []
  const numPoints = Math.min(Math.max(slamData.mapPoints, 10), 5000) // 至少10个点
  
  console.log(`🗺️ 生成地图点: ${numPoints}个点`)
  
  for (let i = 0; i < numPoints; i++) {
    const x = (Math.random() - 0.5) * 20
    const y = (Math.random() - 0.5) * 20
    const z = (Math.random() - 0.5) * 20
    points.push(new THREE.Vector3(x, y, z))
  }
  
  // 释放旧几何体，创建新的几何体以避免BufferGeometry警告
  mapPoints.geometry.dispose()
  mapPoints.geometry = new THREE.BufferGeometry()
  mapPoints.geometry.setFromPoints(points)
  mapPoints.visible = showPoints.value
  
  console.log(`✅ 地图点更新完成: ${points.length}个点`)
}

// 更新关键帧点 - 使用绿色框标记
const updateKeyframePoints = () => {
  if (!keyframePoints) return
  
  // 清除现有的关键帧框
  scene.children.forEach(child => {
    if (child.userData.isKeyframeBox) {
      scene.remove(child)
    }
  })
  
  // 只使用真实轨迹数据meiqid
  let trajectoryPoints = []
  if (slamData.trajectoryPoints && slamData.trajectoryPoints.length > 0) {
    trajectoryPoints = slamData.trajectoryPoints
    console.log(`🔑 使用真实轨迹数据生成绿色框: ${trajectoryPoints.length}个点`)
  } else {
    console.log(`⚠️ 没有轨迹数据，跳过绿色框生成`)
    return
  }
  
  for (let i = 0; i < trajectoryPoints.length; i++) {
    const point = trajectoryPoints[i]
    const x = point.x || 0
    const y = point.y || 0
    const z = point.z || 0
    
    // 创建绿色框几何体 - 当前位置更大更亮
    const isCurrentPosition = (i === trajectoryPoints.length - 1)
    const boxSize = isCurrentPosition ? 0.8 : 0.3
    const boxOpacity = isCurrentPosition ? 1.0 : 0.6
    
    const boxGeometry = new THREE.BoxGeometry(boxSize, boxSize, boxSize)
    const boxMaterial = new THREE.MeshBasicMaterial({ 
      color: 0x00ff00,
      wireframe: true,
      transparent: true,
      opacity: boxOpacity
    })
    
    const keyframeBox = new THREE.Mesh(boxGeometry, boxMaterial)
    keyframeBox.position.set(x, y, z)
    keyframeBox.userData.isKeyframeBox = true
    
    // 为当前位置添加发光效果
    if (isCurrentPosition) {
      const glowGeometry = new THREE.BoxGeometry(1.0, 1.0, 1.0)
      const glowMaterial = new THREE.MeshBasicMaterial({
        color: 0x00ff00,
        transparent: true,
        opacity: 0.3
      })
      const glowBox = new THREE.Mesh(glowGeometry, glowMaterial)
      glowBox.position.set(x, y, z)
      glowBox.userData.isKeyframeBox = true
      scene.add(glowBox)
    }
    
    scene.add(keyframeBox)
  }
  
  console.log(`✅ 轨迹绿色框更新完成: ${trajectoryPoints.length}个框`)
}

// 更新渲染统计
const updateRenderStats = () => {
  let totalVertices = 0
  let totalFaces = 0
  
  scene.traverse((object) => {
    if (object.geometry) {
      if (object.geometry.attributes.position) {
        totalVertices += object.geometry.attributes.position.count
      }
      if (object.geometry.index) {
        totalFaces += object.geometry.index.count / 3
      } else if (object.geometry.attributes.position) {
        totalFaces += object.geometry.attributes.position.count / 3
      }
    }
  })
  
  vertexCount.value = totalVertices
  faceCount.value = Math.round(totalFaces)
}

// 接收父组件状态（由WebRTCStreamView控制）
const updateSlamStatus = (status) => {
  isRunning.value = status === 'running'
  slamStatus.value = status
  console.log('📊 3D可视化状态更新:', status)
}


const resetCamera = () => {
  if (camera && controls) {
    camera.position.set(10, 10, 10)
    camera.lookAt(0, 0, 0)
    controls.reset()
  }
}

const toggleWireframe = () => {
  showWireframe.value = !showWireframe.value
  // 更新材质线框模式
  scene.traverse((object) => {
    if (object.material) {
      object.material.wireframe = showWireframe.value
    }
  })
}

const togglePoints = () => {
  showPoints.value = !showPoints.value
  if (mapPoints) {
    mapPoints.visible = showPoints.value
  }
}

const toggleTrajectory = () => {
  showTrajectory.value = !showTrajectory.value
  if (trajectoryLine) {
    trajectoryLine.visible = showTrajectory.value
  }
}


// 生成3D数据图


// 处理窗口大小变化
const handleResize = () => {
  if (camera && renderer && threeContainer.value) {
    camera.aspect = threeContainer.value.clientWidth / threeContainer.value.clientHeight
    camera.updateProjectionMatrix()
    renderer.setSize(threeContainer.value.clientWidth, threeContainer.value.clientHeight)
  }
}

// 生命周期
onMounted(() => {
  console.log('🎯 3D可视化组件已挂载')
  initThreeJS()
  window.addEventListener('resize', handleResize)
  
  // 延迟初始化数据，确保组件完全加载
  setTimeout(() => {
    console.log('🔄 3D可视化组件初始化完成，开始接收数据')
  }, 1000)
})

onUnmounted(() => {
  if (animationId) {
    cancelAnimationFrame(animationId)
  }
  if (renderer) {
    renderer.dispose()
  }
  window.removeEventListener('resize', handleResize)
})

// 测试轨迹功能
const testTrajectory = () => {
  console.log('🧪 开始测试轨迹生成...')
  
  // 生成测试轨迹数据
  const testTrajectoryPoints = []
  for (let i = 0; i < 20; i++) {
    const t = i / 19
    const x = Math.sin(t * Math.PI * 2) * 3
    const y = Math.cos(t * Math.PI * 2) * 2
    const z = t * 5
    testTrajectoryPoints.push({ x, y, z })
  }
  
  // 更新SLAM数据
  slamData.keyframes = 20
  slamData.mapPoints = 100
  slamData.trajectoryLength = 5.0
  slamData.progress = 50.0
  slamData.trajectoryPoints = testTrajectoryPoints
  
  console.log('🧪 测试数据生成完成:', {
    keyframes: slamData.keyframes,
    mapPoints: slamData.mapPoints,
    trajectoryLength: slamData.trajectoryLength,
    trajectoryPoints: slamData.trajectoryPoints.length
  })
  
  // 强制更新可视化
  updateVisualization()
}

// 重置3D可视化数据
const reset3DVisualization = () => {
  console.log('🔄 重置3D可视化数据...')
  
  // 重置SLAM数据
  slamData.keyframes = 0
  slamData.mapPoints = 0
  slamData.trajectoryLength = 0.0
  slamData.progress = 0.0
  slamData.trajectoryPoints = []
  slamData.mapPoints3D = []
  slamData.cameraPoses = []
  
  // 清除轨迹线
  if (trajectoryLine) {
    trajectoryLine.geometry.dispose()
    trajectoryLine.geometry = new THREE.BufferGeometry()
    trajectoryLine.visible = false
  }
  
  // 清除地图点
  if (mapPoints) {
    mapPoints.geometry.dispose()
    mapPoints.geometry = new THREE.BufferGeometry()
    mapPoints.visible = false
  }
  
  // 清除关键帧点
  if (keyframePoints) {
    keyframePoints.geometry.dispose()
    keyframePoints.geometry = new THREE.BufferGeometry()
    keyframePoints.visible = false
  }
  
  console.log('✅ 3D可视化数据已重置')
  
  // 强制重新渲染
  if (renderer && scene && camera) {
    renderer.render(scene, camera)
  }
}

// 暴露方法给父组件
defineExpose({
  updateSlamData,
  updateSlamStatus,
  testTrajectory,
  reset3DVisualization
})
</script>

<style scoped>
.slam-visualization-3d {
  display: flex;
  height: 100vh;
  background: #1a1a1a;
  color: #ffffff;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

.control-panel {
  width: 300px;
  background: #2a2a2a;
  padding: 20px;
  border-right: 1px solid #444;
  overflow-y: auto;
}

.control-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
}

.control-header h3 {
  margin: 0;
  color: #00ff00;
  font-size: 18px;
}

.status-indicator {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 12px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 500;
}

.status-indicator.running {
  background: #00ff00;
  color: #000;
}

.status-indicator.stopped {
  background: #666;
  color: #fff;
}

.status-indicator.error {
  background: #ff0000;
  color: #fff;
}

.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
}

.control-buttons {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 20px;
}

.btn {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 16px;
  border: none;
  border-radius: 6px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s;
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn-start {
  background: #00ff00;
  color: #000;
}

.btn-start:hover:not(:disabled) {
  background: #00cc00;
}

.btn-stop {
  background: #ff0000;
  color: #fff;
}

.btn-stop:hover:not(:disabled) {
  background: #cc0000;
}

.btn-reset {
  background: #666;
  color: #fff;
}

.btn-reset:hover {
  background: #888;
}

.status-info {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px 16px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border-radius: 8px;
  font-size: 12px;
  font-weight: 500;
}

.btn-test {
  background: #ff6b00;
  color: #fff;
}

.btn-test:hover {
  background: #e55a00;
}

.btn-generate {
  background: #00ff00;
  color: #000;
}

.btn-generate:hover {
  background: #00cc00;
}

.data-stats {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.stat-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 12px;
  background: #333;
  border-radius: 4px;
}

.stat-item label {
  font-size: 12px;
  color: #ccc;
}

.stat-value {
  font-size: 14px;
  font-weight: 600;
  color: #00ff00;
}

.visualization-container {
  flex: 1;
  position: relative;
  background: #1a1a1a;
}

.three-container {
  width: 100%;
  height: 100%;
}

.view-controls {
  position: absolute;
  top: 20px;
  right: 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.view-btn {
  width: 40px;
  height: 40px;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.7);
  color: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.2s;
}

.view-btn:hover {
  background: rgba(0, 0, 0, 0.9);
  transform: scale(1.1);
}

.coordinate-axes {
  position: absolute;
  bottom: 20px;
  left: 20px;
  display: flex;
  gap: 20px;
}

.axis {
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 600;
}

.x-axis {
  background: #ff0000;
  color: #fff;
}

.y-axis {
  background: #00ff00;
  color: #000;
}

.z-axis {
  background: #0000ff;
  color: #fff;
}

.info-panel {
  width: 250px;
  background: #2a2a2a;
  padding: 20px;
  border-left: 1px solid #444;
  overflow-y: auto;
}

.info-section {
  margin-bottom: 20px;
}

.info-section h4 {
  margin: 0 0 12px 0;
  color: #00ff00;
  font-size: 14px;
}

.info-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
  font-size: 12px;
}

.info-item label {
  color: #ccc;
}

.info-item span {
  color: #fff;
  font-weight: 500;
}

/* 图标样式 */
.icon-play::before { content: "▶"; }
.icon-stop::before { content: "⏹"; }
.icon-refresh::before { content: "🔄"; }
.icon-test::before { content: "🧪"; }
.icon-generate::before { content: "🎨"; }
.icon-home::before { content: "🏠"; }
.icon-grid::before { content: "⊞"; }
.icon-dots::before { content: "⋯"; }
.icon-path::before { content: "📈"; }

.test-btn {
  background: #4CAF50;
  color: white;
  border: none;
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 12px;
  margin-top: 8px;
  transition: background 0.3s;
}

.test-btn:hover {
  background: #45a049;
}
</style>
